/***************************
 *  leds_module.c
 *  Cambia las luces del teclado escribiendo en /proc/leds_module un
 *  entero perteneciente a [0,7]
 */
 
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/tty.h>			/* fg_console, MAX_NR_CONSOLES*/
#include <linux/vt_kern.h>		/* fg_console, MAX_NR_CONSOLES*/
#include <linux/kd.h>			/* KDSETLED */
#include <linux/console_struct.h>	/* vc_cons*/

#define MODULE_NAME "leds_module"

static int __init leds_init(void);
static void __exit leds_exit(void);

static struct proc_dir_entry *proc_file;
struct tty_driver *driver;

//se informa al kernel las funciones de inicio y finalizacion
module_init(leds_init);
module_exit(leds_exit);

int leds(struct file *filp, const char __user *buff, unsigned long len, void *data){
    printk(KERN_ALERT "Actualizando leds...\n");
    int n = buff[0] - '0';
	(driver->ioctl) (vc_cons[fg_console].d->vc_tty, 
		NULL, KDSETLED, n);

	return len;
}

/* Inicializacion */
static int __init leds_init() {

    printk(KERN_ALERT "Cargando modulo...");
	
	proc_file = create_proc_entry(MODULE_NAME, 0644, NULL);

	if(proc_file == NULL){
		printk(KERN_ALERT "No se pudo crear el archivo\n");
		return -ENOMEM;
	} 

	proc_file->write_proc = leds;
	driver = vc_cons[fg_console].d->vc_tty->driver;

	printk(KERN_ALERT "listo!.\n");

	return 0;
}

/* Destruccion */
static void __exit leds_exit() {
    printk(KERN_ALERT "Desmontando modulo.\n");
    //removemos el archivo en /proc
	remove_proc_entry(MODULE_NAME, NULL);
}

MODULE_LICENSE("GPL");

